#lang racket

; (* (+ 2 5) 2)

; (and
;   #t
;   (error "foo"))

(define (factorial:v1 n)
  (cond [(= n 0) 1]
        [else (* n (factorial:v1 (- n 1)))]))

; (factorial:v1 10)


(define (if:v1 b l r)
  (cond [b l]
        [else r]))

(define (factorial:v2 n)
  (if:v1 (= n 0)
    1
    (* n (factorial:v2 (- n 1)))))

; (factorial:v2 10)

(define (if:v2 b l r)
  (cond [b (l)]
        [else (r)]))

(define (factorial:v3 n)
  (if:v2 (= n 0)
    (thunk 1)
    (thunk (* n (factorial:v3 (- n 1))))))


; (factorial:v3 10)

(define ten (delay (sleep 1)(display "*\n") 10))

(require rackunit)
(check-equal? (force ten) 10)
(check-equal? (force ten) 10)
(check-equal? (force ten) 10)

(define (thunk-repeat n th)
  (cond [(<= n 0) (void)]
        [else
          (th)
          (thunk-repeat (- n 1) th)]))

; (thunk-repeat 3 (thunk (display "T\n") (sleep 1) 3))

(define (promise-repeat n prom)
  (cond [(<= n 0) (void)]
        [else
          (force prom)
          (promise-repeat (- n 1) prom) ]))
; (promise-repeat 3 (delay (display "P\n") (sleep 1) 3))


(struct promise (body run?) #:transparent)

(define (mk-promise th)
  (promise th #f))

; How do we implement force?
(define (promise-sync p)
  (cond [(promise-run? p) p]
        [else
          (define th (promise-body p))
          (promise (th) #t)]))

(define p1 (mk-promise (lambda () (display "hello world!\n") 10)))

p1

(define p2 (promise-sync p1))

p2

(define (promise-get p)
  (cond [(promise-run? p) (promise-body p)]
        [else (error "Run (promise-sync) first!")]))

(promise-get p2)

(promise-get (promise-sync p1))



(define (i:promise-repeat n prom)
  (cond [(<= n 0) (void)]
        [else
          (i:promise-repeat (- n 1) (promise-sync prom))]))
(i:promise-repeat 3 (mk-promise (thunk (sleep 1) (display "I\n") 3)))




;